using System.Collections.Generic;
using System.Linq;
using data.contracts;
using modelbuilder.parts;
using NUnit.Framework;

namespace modelbuilder.tests.parts
{
    [TestFixture]
    public class ExpandSplitWiresTests
    {
        private ExpandSplitWires sut;
        private IEnumerable<Board> result;

        [SetUp]
        public void Setup() {
            sut = new ExpandSplitWires();
            sut.Out_Result += x => result = x;
        }

        [Test]
        public void To_clause_with_two_simple_semicolon_separated_output_pins() {
            var board = new Board("", "");
            board.Add(new Wire("a.out", "int", "b.in; c.in"));

            sut.In_Process(new[]{board});

            var wires = result.First().Wires.ToArray();
            Assert.That(wires.Length, Is.EqualTo(3));

            Assert.That(wires[0].From, Is.EqualTo("a.out"));
            Assert.That(wires[0].Type, Is.EqualTo("int"));
            Assert.That(wires[0].To, Is.EqualTo("(ISplit<int,int,int>) $split1.Input"));

            Assert.That(wires[1].From, Is.EqualTo("(ISplit<int,int,int>) $split1.Output1"));
            Assert.That(wires[1].Type, Is.EqualTo("int"));
            Assert.That(wires[1].To, Is.EqualTo("b.in"));

            Assert.That(wires[2].From, Is.EqualTo("(ISplit<int,int,int>) $split1.Output2"));
            Assert.That(wires[2].Type, Is.EqualTo("int"));
            Assert.That(wires[2].To, Is.EqualTo("c.in"));
        }

        [Test]
        public void To_clause_with_two_semicolon_separated_output_pins_using_explicit_type() {
            var board = new Board("", "");
            board.Add(new Wire("a.out", "int", "(Foo<t1,t2>) b.in; (Bar<t1,t2>) c.in"));

            sut.In_Process(new[]{board});

            var wires = result.First().Wires.ToArray();
            Assert.That(wires.Length, Is.EqualTo(3));

            Assert.That(wires[0].From, Is.EqualTo("a.out"));
            Assert.That(wires[0].Type, Is.EqualTo("int"));
            Assert.That(wires[0].To, Is.EqualTo("(ISplit<int,int,int>) $split1.Input"));

            Assert.That(wires[1].From, Is.EqualTo("(ISplit<int,int,int>) $split1.Output1"));
            Assert.That(wires[1].Type, Is.EqualTo("int"));
            Assert.That(wires[1].To, Is.EqualTo("(Foo<t1,t2>) b.in"));

            Assert.That(wires[2].From, Is.EqualTo("(ISplit<int,int,int>) $split1.Output2"));
            Assert.That(wires[2].Type, Is.EqualTo("int"));
            Assert.That(wires[2].To, Is.EqualTo("(Bar<t1,t2>) c.in"));
        }

        [Test]
        public void Type_clause_with_type_splitting() {
            var board = new Board("", "");
            board.Add(new Wire("a.out", "Tuple<int*,bool,string> : (Tuple<int*,bool>; string)", "b.in; c.in"));

            sut.In_Process(new[] { board });

            var wires = result.First().Wires.ToArray();
            Assert.That(wires.Length, Is.EqualTo(3));

            Assert.That(wires[0].From, Is.EqualTo("a.out"));
            Assert.That(wires[0].Type, Is.EqualTo("Tuple<int*,bool,string>"));
            Assert.That(wires[0].To, Is.EqualTo("(ISplit<Tuple<int*,bool,string>,Tuple<int*,bool>,string>) $split1.Input"));

            Assert.That(wires[1].From, Is.EqualTo("(ISplit<Tuple<int*,bool,string>,Tuple<int*,bool>,string>) $split1.Output1"));
            Assert.That(wires[1].Type, Is.EqualTo("Tuple<int*,bool>"));
            Assert.That(wires[1].To, Is.EqualTo("b.in"));

            Assert.That(wires[2].From, Is.EqualTo("(ISplit<Tuple<int*,bool,string>,Tuple<int*,bool>,string>) $split1.Output2"));
            Assert.That(wires[2].Type, Is.EqualTo("string"));
            Assert.That(wires[2].To, Is.EqualTo("c.in"));
        }
    }
}